package banksimulator.bank;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;

/**
 * 是<pre>AbstractAccount</pre>的一个简单实现。
 * <br>该帐户能够保存帐户的基本信息，包括帐户ID，用户名，交易记录等。
 * <p>帐户ID的编码序列是当前年月日+4位帐户序列号,如201105150012。
 * @author Rajab Ma 
 */
public class BasicAccount extends AbstractAccount {

    //帐户数目
    private static int accountNum = 0;
    //帐户ID
    private String accountID;
    //帐户名
    private String userName;
    //帐户余额
    private double balance;
    //帐户交易记录
    private List<Transaction> transactionList = new ArrayList<Transaction>();

    /**
     * 根据指定的用户名和初始金额建立一个帐户。
     * @param userName 帐户的用户名
     * @param initBalance 帐户的初始金额
     */
    public BasicAccount(String userName, double initBalance) {
        //帐户ID通过nextAccountID方法获得
        this.accountID = factory.nextAccountID();
        this.userName = userName;
        balance = initBalance;
        Transaction trans = new Transaction(Transaction.Type.ESTABLISH, balance, balance);
        transactionList.add(trans);
        ++accountNum;
    }

    /**
     * 根据指定的用户名建立一个初始金额为0的帐户。
     * @param userName 帐户的用户名
     */
    public BasicAccount(String userName) {
        this(userName, 0.0);
    }
    /**
     *  用于创建BasicAccount类实例的工厂。
     *  <br>生成下一个可用帐户的方法是是当前年月日+4位帐户序列号,如201105150012。
     */
    public static AccountFactory factory = new AccountFactory() {

        public AbstractAccount create(String userName, double initBalance) {
            return new BasicAccount(userName, initBalance);
        }

        public String nextAccountID() {
            //下一个建立的帐户是第几个帐户
            int num = accountNum + 1;
            //取得当前日期
            Date date = new Date();
            SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");

            StringBuilder sb = new StringBuilder();
            String id = "0000" + num;
            //取生成序列的后4位，不足的补0
            id = id.substring(id.length() - 4);
            //生成20110512形式的日期字符串
            sb.append(formatter.format(date));
            sb.append(id);
            return sb.toString();
        }
    };

    public List<Transaction> allTranscation() {
        return Collections.unmodifiableList(transactionList);
    }

    public double getBalance() {
        return balance;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getAccountID() {
        return accountID;
    }

    public void withdraw(double amount) throws FundsInsufficientException {
        if (Double.compare(amount, 0.0) == 0 || amount < 0) {
            //取0元或负数没有意义
            return;
        }
        //余额不足 抛出FundsInsufficientException异常
        if (amount > getBalance()) {
            throw new FundsInsufficientException(getBalance(), amount);
        } else {
            double before = balance;
            balance = balance - amount;
            //加入新的交易记录
            Transaction trans = new Transaction(Transaction.Type.WITHDRAW, amount, balance);
            transactionList.add(trans);
            fireAccountChange(before, balance);
        }
    }

    public void deposit(double amount) {
        if (Double.compare(amount, 0.0) == 0 || amount < 0) {
            //存0元或负数没有意义
            return;
        }
        double before = balance;
        balance = balance + amount;
        //加入新的交易记录
        Transaction trans = new Transaction(Transaction.Type.DEPOSIT, amount, balance);
        transactionList.add(trans);
        fireAccountChange(before, balance);
    }

    @Override
    public String toString() {
        return String.format("ID:%s, Username:%s, Balance:%.2f",
                accountID, userName, balance);
    }

    @Override
    public boolean equals(Object obj) {
        if (obj != null && obj instanceof BasicAccount) {
            BasicAccount other = (BasicAccount) obj;
            return this.accountID.equals(other.accountID);
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 23 * hash + (this.accountID != null ? this.accountID.hashCode() : 0);
        return hash;
    }
}
